/*===========================================================================*\
*                                                                           *
*                               OpenMesh                                    *
*      Copyright (C) 2001-2003 by Computer Graphics Group, RWTH Aachen      *
*                           www.openmesh.org                                *
*                                                                           *
*---------------------------------------------------------------------------*
*                                                                           *
*                                License                                    *
*                                                                           *
*  This library is free software; you can redistribute it and/or modify it  *
*  under the terms of the GNU Library General Public License as published   *
*  by the Free Software Foundation, version 2.                              *
*                                                                           *
*  This library is distributed in the hope that it will be useful, but      *
*  WITHOUT ANY WARRANTY; without even the implied warranty of               *
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
*  Library General Public License for more details.                         *
*                                                                           *
*  You should have received a copy of the GNU Library General Public        *
*  License along with this library; if not, write to the Free Software      *
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                *
*                                                                           *
\*===========================================================================*/

#ifndef OPENMESH_PROPERTY_HH
#define OPENMESH_PROPERTY_HH


//== INCLUDES =================================================================


#include <Core/System/config.hh>
#include <Core/System/omstream.hh>
#include <Core/Mesh/Kernels/Common/Handles.hh>
#include <Core/IO/StoreRestore.hh>
#include <vector>
#include <string>
#include <algorithm>


//== FORWARDDECLARATIONS ======================================================

namespace OpenMesh {
	class BaseKernel;
}


//== NAMESPACES ===============================================================

namespace OpenMesh {


	//== CLASS DEFINITION =========================================================

	/** \class BaseProperty Property.hh <Core/Utils/PropertyT.hh>

	Abstract class defining the basic interface of a dynamic property.
	**/

	class BaseProperty
	{
	public:

		/// Indicates an error when a size is returned by a member.
		static const size_t UnknownSize = size_t(-1);

	public:

		/// \brief Default constructor.
		///
		/// In %OpenMesh all mesh data is stored in so-called properties.
		/// We distinuish between standard properties, which can be defined at
		/// compile time using the Attributes in the traits definition and
		/// at runtime using the request property functions defined in one of
		/// the kernels.
		///
		/// If the property should be stored along with the default properties
		/// in the OM-format one must name the property and enable the persistant
		/// flag with set_persistent().
		///
		/// \param _name Optional textual name for the property.
		///
		BaseProperty(const std::string& _name = "<unknown>")
			: name_(_name), persistent_(false) {}

		/// \brief Copy constructor
		BaseProperty(const BaseProperty & _rhs)
			: name_( _rhs.name_ ), persistent_( _rhs.persistent_ ) {}

		/// Destructor.
		virtual ~BaseProperty() {}

	public: // synchronized array interface

		/// Reserve memory for n elements.
		virtual void reserve(size_t _n) = 0;

		/// Resize storage to hold n elements.
		virtual void resize(size_t _n) = 0;

		/// Free unused memory.
		virtual void free_mem() = 0;

		/// Extend the number of elements by one.
		virtual void push_back() = 0;

		/// Let two elements swap their storage place.
		virtual void swap(size_t _i0, size_t _i1) = 0;

		/// Return a deep copy of self.
		virtual BaseProperty* clone () const = 0;

	public: // named property interface

		/// Return the name of the property
		const std::string& name() const { return name_; }

		virtual void stats(std::ostream& _ostr) const;

	public: // I/O support

		/// Returns true if the persistent flag is enabled else false.
		bool persistent(void) const { return persistent_; }

		/// Enable or disable persistency. Self must be a named property to enable
		/// persistency.
		virtual void set_persistent( bool _yn ) = 0;

		/// Number of elements in property
		virtual size_t       n_elements() const = 0;

		/// Size of one element in bytes or UnknownSize if not known.
		virtual size_t       element_size() const = 0;

		/// Return size of property in bytes
		virtual size_t       size_of() const
		{
			return size_of( n_elements() );
		}

		/// Estimated size of property if it has _n_elem elements.
		/// The member returns UnknownSize if the size cannot be estimated.
		virtual size_t       size_of(size_t _n_elem) const
		{
			return (element_size()!=UnknownSize)
				? (_n_elem*element_size())
				: UnknownSize;
		}

		/// Store self as one binary block
		virtual size_t store( std::ostream& _ostr, bool _swap ) const = 0;

		/** Restore self from a binary block. Uses reserve() to set the
		size of self before restoring.
		**/
		virtual size_t restore( std::istream& _istr, bool _swap ) = 0;

	protected:

		// To be used in a derived class, when overloading set_persistent()
		template < typename T >
		void check_and_set_persistent( bool _yn )
		{
			if ( _yn && !IO::is_streamable<T>() )
				omerr() << "Warning! Type of property value is not binary storable!\n";
			persistent_ = IO::is_streamable<T>() && _yn;
		}

	private:

		std::string name_;
		bool        persistent_;
	};

	//-----------------------------------------------------------------------------


	/** \class PropertyT Property.hh <Core/Utils/PropertyT.hh>
	*
	*  \brief Default property class for any type T.
	*
	*  The default property class for any type T.
	*
	*  The property supports persistency if T is a "fundamental" type:
	*  - integer fundamental types except bool:
	*    char, short, int, long, long long (__int64 for MS VC++) and
	*    their unsigned companions.
	*  - float fundamentals except <tt>long double</tt>:
	*    float, double
	*  - %OpenMesh vector types
	*
	*  Persistency of non-fundamental types is supported if and only if a
	*  specialization of struct IO::binary<> exists for the wanted type.
	*/
	template <class T>
	class PropertyT : public BaseProperty
	{
	public:

		typedef T                                       Value;
		typedef std::vector<T>                          vector_type;
		typedef T                                       value_type;
		typedef typename vector_type::reference         reference;
		typedef typename vector_type::const_reference   const_reference;

	public:

		/// Default constructor
		PropertyT(const std::string& _name = "<unknown>")
			: BaseProperty(_name)
		{}

		/// Copy constructor
		PropertyT(const PropertyT & _rhs)
			: BaseProperty( _rhs ), data_( _rhs.data_ ) {}


	public: // inherited from BaseProperty

		virtual void reserve(size_t _n) { data_.reserve(_n);    }
		virtual void resize(size_t _n)  { data_.resize(_n);     }
		virtual void push_back()        { data_.push_back(T()); }
		virtual void free_mem()         { vector_type(data_).swap(data_); }
		virtual void swap(size_t _i0, size_t _i1)
		{ std::swap(data_[_i0], data_[_i1]); }

	public:

		virtual void set_persistent( bool _yn )
		{ check_and_set_persistent<T>( _yn ); }

		virtual size_t       n_elements()   const { return data_.size(); }
		virtual size_t       element_size() const { return IO::size_of<T>(); }

#ifndef DOXY_IGNORE_THIS
		struct plus {
			size_t operator () ( size_t _b, const T& _v )
			{ return _b + IO::size_of<T>(_v); }
		};
#endif

		virtual size_t size_of(void) const
		{
			if (element_size() != IO::UnknownSize)
				return this->BaseProperty::size_of(n_elements());
			return std::accumulate(data_.begin(), data_.end(), static_cast<size_t>(0), plus());
		}

		virtual size_t size_of(size_t _n_elem) const
		{ return this->BaseProperty::size_of(_n_elem); }

		virtual size_t store( std::ostream& _ostr, bool _swap ) const
		{
			if ( IO::is_streamable<vector_type>() )
				return IO::store(_ostr, data_, _swap );
			size_t bytes = 0;
			for (size_t i=0; i<n_elements(); ++i)
				bytes += IO::store( _ostr, data_[i], _swap );
			return bytes;
		}

		virtual size_t restore( std::istream& _istr, bool _swap )
		{
			if ( IO::is_streamable<vector_type>() )
				return IO::restore(_istr, data_, _swap );
			size_t bytes = 0;
			for (size_t i=0; i<n_elements(); ++i)
				bytes += IO::restore( _istr, data_[i], _swap );
			return bytes;
		}

	public: // data access interface

		/// Get pointer to array (does not work for T==bool)
		const T* data() const { return &data_[0]; }

		/// Access the i'th element. No range check is performed!
		reference operator[](int _idx)
		{
			assert( size_t(_idx) < data_.size() );
			return data_[_idx];
		}

		/// Const access to the i'th element. No range check is performed!
		const_reference operator[](int _idx) const
		{
			assert( size_t(_idx) < data_.size());
			return data_[_idx];
		}

		/// Make a copy of self.
		PropertyT<T>* clone() const
		{
			PropertyT<T>* p = new PropertyT<T>( *this );
			return p;
		}


	private:

		vector_type data_;
	};

	//-----------------------------------------------------------------------------


	/** \class PropertyT<bool> Property.hh <Core/Utils/PropertyT.hh>

	Property specialization for bool type. The data will be stored as
	a bitset.
	*/
	template <>
	class PropertyT<bool> : public BaseProperty
	{
	public:

		typedef std::vector<bool>                       vector_type;
		typedef bool                                    value_type;
		typedef vector_type::reference                  reference;
		typedef vector_type::const_reference            const_reference;

	public:

		PropertyT(const std::string& _name = "<unknown>")
			: BaseProperty(_name)
		{ }

		PropertyT(const PropertyT & _rhs)
			: BaseProperty( _rhs ), data_( _rhs.data_ ) {}

	public: // inherited from BaseProperty

		virtual void reserve(size_t _n) { data_.reserve(_n);    }
		virtual void resize(size_t _n)  { data_.resize(_n);     }
		virtual void push_back()        { data_.push_back(bool()); }
		virtual void free_mem()         { vector_type(data_).swap(data_); }
		virtual void swap(size_t _i0, size_t _i1)
		{ bool t(data_[_i0]); data_[_i0]=data_[_i1]; data_[_i1]=t; }

	public:

		virtual void set_persistent( bool _yn )
		{
			check_and_set_persistent<bool>( _yn );
		}

		virtual size_t       n_elements()   const { return data_.size();  }
		virtual size_t       element_size() const { return UnknownSize;    }
		virtual size_t       size_of() const      { return size_of( n_elements() ); }
		virtual size_t       size_of(size_t _n_elem) const
		{
			return _n_elem / 8 + ((_n_elem % 8)!=0);
		}

		size_t store( std::ostream& _ostr, bool _swap ) const
		{
			_swap;
			size_t bytes = 0;

			size_t N = data_.size() / 8;
			size_t R = data_.size() % 8;

			size_t        idx;  // element index
			size_t        bidx;
			unsigned char bits; // bitset

			for (bidx=idx=0; idx < N; ++idx, bidx+=8)
			{
				bits = (!!data_[bidx]) | (!!data_[bidx+1] << 1) | (!!data_[bidx+2] << 2) | (!!data_[bidx+3] << 3) | (!!data_[bidx+4] << 4) | (!!data_[bidx+5] << 5) | (!!data_[bidx+6] << 6) | (!!data_[bidx+7] << 7);
				_ostr << bits;
			}
			bytes = N;

			if (R)
			{
				bits = 0;
				for (idx=0; idx < R; ++idx)
					bits |= !!data_[bidx+idx] << idx;
				_ostr << bits;
				++bytes;
			}

			std::cout << std::endl;

			assert( bytes == size_of() );

			return bytes;
		}

		size_t restore( std::istream& _istr, bool _swap )
		{
			_swap;
			size_t bytes = 0;

			size_t N = data_.size() / 8;
			size_t R = data_.size() % 8;

			size_t        idx;  // element index
			size_t        bidx; //
			unsigned char bits; // bitset

			for (bidx=idx=0; idx < N; ++idx, bidx+=8)
			{
				_istr >> bits;
				data_[bidx+0] = !!(bits & 0x01);
				data_[bidx+1] = !!(bits & 0x02);
				data_[bidx+2] = !!(bits & 0x04);
				data_[bidx+3] = !!(bits & 0x08);
				data_[bidx+4] = !!(bits & 0x10);
				data_[bidx+5] = !!(bits & 0x20);
				data_[bidx+6] = !!(bits & 0x40);
				data_[bidx+7] = !!(bits & 0x80);
			}
			bytes = N;

			if (R)
			{
				_istr >> bits;
				for (idx=0; idx < R; ++idx)
					data_[bidx+idx] = !!(bits & (1<<idx));
				++bytes;
			}

			std::cout << std::endl;

			return bytes;
		}


	public:

		/// Access the i'th element. No range check is performed!
		reference operator[](int _idx)
		{
			assert( size_t(_idx) < data_.size() );
			return data_[_idx];
		}

		/// Const access to the i'th element. No range check is performed!
		const_reference operator[](int _idx) const
		{
			assert( size_t(_idx) < data_.size());
			return data_[_idx];
		}

		/// Make a copy of self.
		PropertyT<bool>* clone() const
		{
			PropertyT<bool>* p = new PropertyT<bool>( *this );
			return p;
		}


	private:

		vector_type data_;
	};


	//-----------------------------------------------------------------------------


	/** \class PropertyT<std::string> Property.hh <Core/Utils/PropertyT.hh>

	Property specialization for std::string type.
	*/
	template <>
	class PropertyT<std::string> : public BaseProperty
	{
	public:

		typedef std::string                             Value;
		typedef std::vector<std::string>                vector_type;
		typedef std::string                             value_type;
		typedef vector_type::reference                  reference;
		typedef vector_type::const_reference            const_reference;

	public:

		PropertyT(const std::string& _name = "<unknown>")
			: BaseProperty(_name)
		{ }

		PropertyT(const PropertyT & _rhs)
			: BaseProperty( _rhs ), data_( _rhs.data_ ) {}


	public: // inherited from BaseProperty

		virtual void reserve(size_t _n) { data_.reserve(_n);    }
		virtual void resize(size_t _n)  { data_.resize(_n);     }
		virtual void push_back()        { data_.push_back(std::string()); }
		virtual void free_mem()         { vector_type(data_).swap(data_); }
		virtual void swap(size_t _i0, size_t _i1) {
			std::swap(data_[_i0], data_[_i1]);
		}

	public:

		virtual void set_persistent( bool _yn )
		{ check_and_set_persistent<std::string>( _yn ); }

		virtual size_t       n_elements()   const { return data_.size();  }
		virtual size_t       element_size() const { return UnknownSize;    }
		virtual size_t       size_of() const
		{ return IO::size_of( data_ ); }

		virtual size_t       size_of(size_t _n_elem) const
		{ _n_elem; return UnknownSize; }

		/// Store self as one binary block. Max. length of a string is 65535 bytes.
		size_t store( std::ostream& _ostr, bool _swap ) const
		{ return IO::store( _ostr, data_, _swap ); }

		size_t restore( std::istream& _istr, bool _swap )
		{ return IO::restore( _istr, data_, _swap ); }

	public:

		const value_type* data() const { return (value_type*) &data_[0]; }

		/// Access the i'th element. No range check is performed!
		reference operator[](int _idx) {
			assert( size_t(_idx) < data_.size());
			return ((value_type*) &data_[0])[_idx];
		}

		/// Const access the i'th element. No range check is performed!
		const_reference operator[](int _idx) const {
			assert( size_t(_idx) < data_.size());
			return ((value_type*) &data_[0])[_idx];
		}

		PropertyT<value_type>* clone() const {
			PropertyT<value_type>* p = new PropertyT<value_type>( *this );
			return p;
		}


	private:

		vector_type data_;

	};


	//== CLASS DEFINITION =========================================================


	/// Base property handle.
	template <class T>
	struct BasePropHandleT : public BaseHandle
	{
		typedef T                                       Value;
		typedef std::vector<T>                          vector_type;
		typedef T                                       value_type;
		typedef typename vector_type::reference         reference;
		typedef typename vector_type::const_reference   const_reference;

		explicit BasePropHandleT(int _idx=-1) : BaseHandle(_idx) {}
	};


	/** \ingroup mesh_property_handle_group
	*  Handle representing a vertex property
	*/
	template <class T>
	struct VPropHandleT : public BasePropHandleT<T>
	{
		typedef T                       Value;
		typedef T                       value_type;

		explicit VPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
		explicit VPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
	};


	/** \ingroup mesh_property_handle_group
	*  Handle representing a halfedge property
	*/
	template <class T>
	struct HPropHandleT : public BasePropHandleT<T>
	{
		typedef T                       Value;
		typedef T                       value_type;

		explicit HPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
		explicit HPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
	};


	/** \ingroup mesh_property_handle_group
	*  Handle representing an edge property
	*/
	template <class T>
	struct EPropHandleT : public BasePropHandleT<T>
	{
		typedef T                       Value;
		typedef T                       value_type;

		explicit EPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
		explicit EPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
	};


	/** \ingroup mesh_property_handle_group
	*  Handle representing a face property
	*/
	template <class T>
	struct FPropHandleT : public BasePropHandleT<T>
	{
		typedef T                       Value;
		typedef T                       value_type;

		explicit FPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
		explicit FPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
	};


	/** \ingroup mesh_property_handle_group
	*  Handle representing a mesh property
	*/
	template <class T>
	struct MPropHandleT : public BasePropHandleT<T>
	{
		typedef T                       Value;
		typedef T                       value_type;

		explicit MPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
		explicit MPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
	};



	//-----------------------------------------------------------------------------


	/// A a container for properties.
	class PropertyContainer
	{
	public:

		//-------------------------------------------------- constructor / destructor

		PropertyContainer() {}
		virtual ~PropertyContainer() { clear(); }


		//------------------------------------------------------------- info / access

		typedef std::vector<BaseProperty*> Properties;
		const Properties& properties() const { return properties_; }
		size_t size() const { return properties_.size(); }



		//--------------------------------------------------------- copy / assignment

		PropertyContainer(const PropertyContainer& _rhs) { operator=(_rhs); }

		PropertyContainer& operator=(const PropertyContainer& _rhs)
		{
			clear();
			properties_ = _rhs.properties_;
			Properties::iterator p_it=properties_.begin(), p_end=properties_.end();
			for (; p_it!=p_end; ++p_it)
				if (*p_it)
					*p_it = (*p_it)->clone();
			return *this;
		}



		//--------------------------------------------------------- manage properties

		template <class T>
		BasePropHandleT<T> add(const T&, const std::string& _name="<unknown>")
		{
			Properties::iterator p_it=properties_.begin(), p_end=properties_.end();
			int idx=0;
			for (; p_it!=p_end && *p_it!=NULL; ++p_it, ++idx);
			if (p_it==p_end) properties_.push_back(NULL);
			properties_[idx] = new PropertyT<T>(_name);
			return BasePropHandleT<T>(idx);
		}


		template <class T>
		BasePropHandleT<T> handle(const T&, const std::string& _name) const
		{
			Properties::const_iterator p_it = properties_.begin();
			for (int idx=0; p_it != properties_.end(); ++p_it, ++idx)
			{
				if (*p_it != NULL && (*p_it)->name() == _name && //skip deleted properties
					dynamic_cast<PropertyT<T>*>(properties_[idx]) != NULL)//check type
				{
					return BasePropHandleT<T>(idx);
				}
			}
			return BasePropHandleT<T>();
		}

		BaseProperty* property( const std::string& _name ) const
		{
			Properties::const_iterator p_it = properties_.begin();
			for (int idx=0; p_it != properties_.end(); ++p_it, ++idx)
			{
				if (*p_it != NULL && (*p_it)->name() == _name) //skip deleted properties
				{
					return *p_it;
				}
			}
			return NULL;
		}

		template <class T> PropertyT<T>& property(BasePropHandleT<T> _h)
		{
			assert(_h.idx() >= 0 && _h.idx() < (int)properties_.size());
			assert(properties_[_h.idx()] != NULL);
#ifdef NDEBUG
			return *static_cast  <PropertyT<T>*> (properties_[_h.idx()]);
#else
			PropertyT<T>* p = dynamic_cast<PropertyT<T>*>(properties_[_h.idx()]);
			assert(p != NULL);
			return *p;
#endif
		}


		template <class T> const PropertyT<T>& property(BasePropHandleT<T> _h) const
		{
			assert(_h.idx() >= 0 && _h.idx() < (int)properties_.size());
			assert(properties_[_h.idx()] != NULL);
#ifdef NDEBUG
			return *static_cast<PropertyT<T>*>(properties_[_h.idx()]);
#else
			PropertyT<T>* p = dynamic_cast<PropertyT<T>*>(properties_[_h.idx()]);
			assert(p != NULL);
			return *p;
#endif
		}


		template <class T> void remove(BasePropHandleT<T> _h)
		{
			assert(_h.idx() >= 0 && _h.idx() < (int)properties_.size());
			delete properties_[_h.idx()];
			properties_[_h.idx()] = NULL;
		}


		void clear()
		{
			std::for_each(properties_.begin(), properties_.end(), Delete());
		}


		//---------------------------------------------------- synchronize properties

		void reserve(size_t _n) const {
			std::for_each(properties_.begin(), properties_.end(), Reserve(_n));
		}

		void resize(size_t _n) const {
			std::for_each(properties_.begin(), properties_.end(), Resize(_n));
		}

		void free_mem() const {
			std::for_each(properties_.begin(), properties_.end(), FreeMem());
		}

		void swap(size_t _i0, size_t _i1) const {
			std::for_each(properties_.begin(), properties_.end(), Swap(_i0, _i1));
		}



	protected: // generic add/get

		size_t _add( BaseProperty* _bp )
		{
			Properties::iterator p_it=properties_.begin(), p_end=properties_.end();
			size_t idx=0;
			for (; p_it!=p_end && *p_it!=NULL; ++p_it, ++idx);
			if (p_it==p_end) properties_.push_back(NULL);
			properties_[idx] = _bp;
			return idx;
		}

		BaseProperty& _property( size_t _idx )
		{
			assert( _idx < properties_.size());
			assert( properties_[_idx] != NULL);
			BaseProperty *p = properties_[_idx];
			assert( p != NULL );
			return *p;
		}

		const BaseProperty& _property( size_t _idx ) const
		{
			assert( _idx < properties_.size());
			assert( properties_[_idx] != NULL);
			BaseProperty *p = properties_[_idx];
			assert( p != NULL );
			return *p;
		}


		typedef Properties::iterator       iterator;
		typedef Properties::const_iterator const_iterator;
		iterator begin() { return properties_.begin(); }
		iterator end()   { return properties_.end(); }
		const_iterator begin() const { return properties_.begin(); }
		const_iterator end() const   { return properties_.end(); }

		friend class BaseKernel;

	private:

		//-------------------------------------------------- synchronization functors

#ifndef DOXY_IGNORE_THIS

		struct Reserve
		{
			Reserve(size_t _n) : n_(_n) {}
			void operator()(BaseProperty* _p) const { if (_p) _p->reserve(n_); }
			size_t n_;
		};

		struct Resize
		{
			Resize(size_t _n) : n_(_n) {}
			void operator()(BaseProperty* _p) const { if (_p) _p->resize(n_); }
			size_t n_;
		};

		struct FreeMem
		{
			FreeMem() {}
			void operator()(BaseProperty* _p) const { if (_p) _p->free_mem(); }
		};

		struct Swap
		{
			Swap(size_t _i0, size_t _i1) : i0_(_i0), i1_(_i1) {}
			void operator()(BaseProperty* _p) const { if (_p) _p->swap(i0_, i1_); }
			size_t i0_, i1_;
		};

		struct Delete
		{
			Delete() {}
			void operator()(BaseProperty* _p) const { if (_p) delete _p; _p=NULL; }
		};

#endif

		Properties   properties_;
	};



	//=============================================================================
} // namespace OpenMesh
//=============================================================================
#endif // OPENMESH_PROPERTY_HH defined
//=============================================================================
